#include "CAudioIC.h"

extern CAudioIC g_AudioIC;

#if 0
void printfmem(char *pBuffer, int size)
{
    int i;
    for(i = 0; i < size; i++){
        if(0 == i%16){
            kprintf("\n");
        }
        kprintf("0x%x ", pBuffer[i]);
    }
    kprintf("\n");
}
#endif

Int32 WriteBufferThread(void *p)
{
    kprintf("WriteBufferThread on!\n");
    g_AudioIC.m_pFIFOPlayBuffer->WriteThreadFunc();

    return 1;
}

void CFIFOPlayBuffer::SetFlagPoint(int Mp3WRBufferNO)
{
    m_pStart = m_WRBuffer[Mp3WRBufferNO].Buffer;
    m_pProtect = m_WRBuffer[Mp3WRBufferNO].Buffer + FIFO_INPUT_BUFFER_SIZE;
    m_pEnd = m_WRBuffer[Mp3WRBufferNO].Buffer + FIFO_SINGLE_BUFFER_SIZE;
//    kprintf("Buffer:0x%x, pStart:0x%x, pProtect:0x%x, pEnd:0x%x.\n",
//        m_WRBuffer[Mp3WRBufferNO].Buffer, m_pStart, m_pProtect, m_pEnd);
}

void CFIFOPlayBuffer::ResetAll()
{
    m_WRBuffer[0].ReadWriteTag = 0;
    m_WRBuffer[1].ReadWriteTag = 0;
    m_pIWThread = NULL;
    m_WriteThreadRun = 1;
    m_iChangedBufferSignal = 0;
    m_iCurrentReadBufferNO = 0;
    m_iCurrentWriteBufferNO = 0;
    m_uLastOffset = 0;
    SetFlagPoint(0);
    m_pCurrentPlayAddr = m_pStart;
//    kprintf("m_WRBuffer[0].Buffer:0x%x.\n", m_WRBuffer[0].Buffer );
//    kprintf("m_WRBuffer[1].Buffer:0x%x.\n", m_WRBuffer[1].Buffer );
}

CFIFOPlayBuffer::CFIFOPlayBuffer()
{
    ResetAll();
}

ECode CFIFOPlayBuffer::FillPlayBuffer(Mp3WRBuffer *pWRBuffer, int* pReadedSize)
{
    ECode ec;
    int iReadedSize;
    ec = g_AudioIC.m_FileReader.ReadFile(
        pWRBuffer->Buffer+FIFO_PROTECT_BUFFER_SIZE,
        FIFO_INPUT_BUFFER_SIZE,
        &iReadedSize);
    if(FAILED(ec)){
        kprintf("FillPlayBuffer ReadFile FAILED. ec:0x%x.\n", ec);
        return ec;
    }
    pWRBuffer->ReadWriteTag = 1;

    if(NULL != pReadedSize){
        *pReadedSize = iReadedSize;
    }

    if(iReadedSize < FIFO_INPUT_BUFFER_SIZE){
        kprintf("File must end, last read lenth:0x%x.\n", iReadedSize);
    }
    return ec;
}

ECode CFIFOPlayBuffer::NewFillPlayBuffer(Uint32 uFileOffset)
{
    ECode ec;

    int iReadedSize;

    g_AudioIC.m_FileReader.SetFileOffset(uFileOffset);

    ec = g_AudioIC.m_FileReader.ReadFile(
        m_WRBuffer[0].Buffer,
        FIFO_SINGLE_BUFFER_SIZE,
        &iReadedSize);
    if(FAILED(ec)){
        kprintf("CFIFOPlayBuffer NewFillPlayBuffer ReadFile FAILED. ec:0x%x.\n", ec);
        return ec;
    }
    m_WRBuffer[0].ReadWriteTag = 1;

    m_iCurrentWriteBufferNO = 1;

    return ec;
}

ECode CFIFOPlayBuffer::StartWriteThread()
{
    ECode ec;

    if(NULL != m_pIWThread){
        kprintf("CFIFOPlayBuffer StartWriteThread Kill prior thread.\n");
        StopWriteThread();
    }

    ResetAll();

    ec = NewFillPlayBuffer(0);
    if(FAILED(ec)){
        kprintf("CFIFOPlayBuffer StartWriteThread  NewFillPlayBuffer failed!\n");
        return ec;
    }

    ec = DzCreateKernelThread(WriteBufferThread,
                              NULL,
                              CreateThreadFlag_System,
                              ThreadPriorityRank_SoftRealTime,
                              &m_pIWThread);
    if(FAILED(ec)){
        kprintf("CFIFOPlayBuffer StartWriteThread  DzCreateKernelThread failed!\n");
        return ec;
    }

    return ec;
}

ECode CFIFOPlayBuffer::StopWriteThread()
{
    m_WriteThreadRun = 0;
    if(NULL != m_pIWThread){
        WaitResult Result;
        m_pIWThread->Interrupt();
        m_pIWThread->Join(10000, &Result);
        m_pIWThread = NULL;
    }
    ResetAll();
    return NOERROR;
}


void CFIFOPlayBuffer::GetNormalPlayAddr(Uint32 uCurrentOffset)
{
    int iRestBufferSize;

    m_pCurrentPlayAddr += (uCurrentOffset - m_uLastOffset);
    if(m_pCurrentPlayAddr < m_pProtect){
//        kprintf("GetNormalPlayAddr OK\n");
        goto EXIT;
    }

    //Into protect section, change to another buffer.
    iRestBufferSize = m_pCurrentPlayAddr - m_pProtect;

    m_WRBuffer[BUFFER_NUMBER_TO_TAG(m_iCurrentReadBufferNO)].ReadWriteTag = 0;
    m_iCurrentReadBufferNO++;
    //buffer should be ready.
    while(1 != m_WRBuffer[BUFFER_NUMBER_TO_TAG(m_iCurrentReadBufferNO)].ReadWriteTag){
        kprintf("CFIFOPlayBuffer GetAddr Read File not ready.\n");
        nu_sleep(DzMillisecondsToTicks(10));
    }
    SetFlagPoint(BUFFER_NUMBER_TO_TAG(m_iCurrentReadBufferNO));
    m_pCurrentPlayAddr = m_pStart + iRestBufferSize;
    nu_notifyDeviceCallerByHISR((Uint32)&m_WriteEvent, SIGNALED);
//    kprintf("m_iCurrentReadBufferNO:%d\n", m_iCurrentReadBufferNO);


EXIT:
    m_uLastOffset = uCurrentOffset;

}

void CFIFOPlayBuffer::ChangePlayOffset(Uint32 uCurrentOffset)
{
    m_uLastOffset = uCurrentOffset;
    m_iCurrentReadBufferNO = 0;
    SetFlagPoint(0);
    m_pCurrentPlayAddr = m_pStart;
    nu_notifyDeviceCallerByHISR((Uint32)&m_WriteEvent, CHANGE_BUFFER_SIGNALED);
    m_iChangedBufferSignal = 1;
    while(0 == m_iChangedBufferSignal){
//        May Dead!
//        nu_sleep(DzMillisecondsToTicks(10));
    }
    kprintf("ChangePlayOffset OK\n");
}


Byte *CFIFOPlayBuffer::GetAddr(Uint32 uCurrentOffset)
{
    if((m_uLastOffset > uCurrentOffset)
        ||((uCurrentOffset - m_uLastOffset) > (Uint32)(m_pEnd - m_pCurrentPlayAddr))){
        // forward or rewind
        kprintf("GetAddr forward or rewind\n");
        ChangePlayOffset(uCurrentOffset);
        return m_pCurrentPlayAddr;
    }

    GetNormalPlayAddr(uCurrentOffset);

    return m_pCurrentPlayAddr;
}


ECode CFIFOPlayBuffer::ChangePlayBuffer()
{

    ECode ec;
    m_WRBuffer[0].ReadWriteTag = 0;
    m_WRBuffer[1].ReadWriteTag = 0;

    m_iCurrentWriteBufferNO = 0;

    ec = NewFillPlayBuffer(m_uLastOffset);
    if(FAILED(ec)){
        kprintf("CFIFOPlayBuffer ChangePlayBuffer NewFillPlayBuffer FAILED. ec:0x%x.\n", ec);
        return ec;
    }


    if(g_AudioIC.m_FileReader.GetFileOffset() < g_AudioIC.m_FileReader.m_iFileSize){
        //Start to fill next play buffer.
        nu_notifyDeviceCaller((Uint32)&m_WriteEvent, SIGNALED);
    }

    //BUGBUG:Need send failed info to callback function
    m_iChangedBufferSignal = 0;

    return ec;
}


ECode CFIFOPlayBuffer::WriteThreadFunc()
{
    //Fill the other buffer
    ECode ec;
    WaitResult Result;
    EventState State;

    if(g_AudioIC.m_FileReader.GetFileOffset() < g_AudioIC.m_FileReader.m_iFileSize){
        //Start to fill next play buffer.
        nu_notifyDeviceCaller((Uint32)&m_WriteEvent, SIGNALED);
    }
    kprintf("WriteThreadFunc start!\n");
    do{
        m_WriteEvent.Wait(&Result, &State);
//        kprintf("m_iCurrentWriteBufferNO:%d\n", m_iCurrentWriteBufferNO);
        if(WaitResult_Interrupted == Result){
            kprintf("CFIFOPlayBuffer WriteThread Interrupted.\n");
            continue;
        }

        if(CHANGE_BUFFER_SIGNALED == State){
            kprintf("CFIFOPlayBuffer WriteThread ChangePlayBuffer.\n");
            ChangePlayBuffer();
            continue;
        }

        assert(0 == m_WRBuffer[BUFFER_NUMBER_TO_TAG(m_iCurrentWriteBufferNO)].ReadWriteTag);

        //Copy the protect section buffer
        memcpy(m_WRBuffer[BUFFER_NUMBER_TO_TAG(m_iCurrentWriteBufferNO)].Buffer,
            m_WRBuffer[BUFFER_NUMBER_TO_TAG(m_iCurrentWriteBufferNO-1)].Buffer+FIFO_INPUT_BUFFER_SIZE,
            FIFO_PROTECT_BUFFER_SIZE);
        ec = FillPlayBuffer(&m_WRBuffer[BUFFER_NUMBER_TO_TAG(m_iCurrentWriteBufferNO)],
             NULL);
        if(FAILED(ec)){
            kprintf("CFIFOPlayBuffer WriteThreadFunc FillPlayBuffer %d failed!\n"
                ,BUFFER_NUMBER_TO_TAG(m_iCurrentWriteBufferNO));
        }else {
            m_iCurrentWriteBufferNO++;
        }

    }while(m_WriteThreadRun);
    kprintf("CFIFOPlayBuffer WriteThreadFunc end.\n");
    return NOERROR;
}

